import { AbstractClientBase, Metadata } from 'grpc-web'

interface DefaultsConfigType {
  metadata?: Metadata | null;
  timeout?: number | null;
  baseURL?: string;
}

interface ResponseDataType {
  data: any;
  status?: number;
  statusText?: string;
  request?: any;
  config?: any;
}

/**
 * 处理metadata数据
 * @param metadata
 * @return newMetadata
 */
function resolveMetadata(metadata): Metadata {
  if (!(metadata instanceof Object)) return {}
  const newMetadata: Metadata = {}
  Object.entries(metadata).forEach(item => {
    // 转码成字符串
    newMetadata[item[0]] = (item[1] instanceof Object) ? JSON.stringify(item[1]) : encodeURIComponent(String(item[1]))
  })
  return newMetadata
}

class Interceptors {
  interceptorList: null | Map<Function, Array<Function>>
  /**
   * 创建拦截器示例
   */
  constructor() {
    this.interceptorList = new Map()
  }
  /**
   * 添加拦截器函数
   * @param configFunc {function} 拦截请求体或响应体
   * @param errFunction {function} 错误的拦截器函数
   */
  use(configFunc: Function, errFunction?: Function) {
    if (typeof configFunc === 'function') {
      this.interceptorList.set(configFunc, [configFunc, errFunction])
    }
  }
  /**
   * 删除拦截器
   * @param configFunc {function} 拦截请求体或响应体的函数
   */
  delete(configFunc: Function) {
    this.interceptorList.delete(configFunc)
  }
  /**
   * 清除所有拦截器
   */
  clearAll () {
    this.interceptorList.clear()
  }
}

export default class GrpcCreator {
  private client: AbstractClientBase
  // CA证书
  private credentials: null | { [index: string]: string; }
  static defaults: null | DefaultsConfigType
  // 拦截器
  static interceptors: {
    // 请求拦截器
    request: Interceptors | null
    // 响应拦截器
    response: Interceptors | null
  }

  /**
   * 创建 grpc clien
   * @param clientClass 客户端类型
   * @param backendUrl {string} 后端地址
   * @param credentials {null | { [index: string]: string; }} 证书数组
   */
  constructor(clientClass, backendUrl?: string, credentials?: null | { [index: string]: string; }) {
    this.credentials = credentials
    this.client = new clientClass(backendUrl || GrpcCreator.defaults.baseURL, this.credentials)
  }

  /**
   * 请求拦截器: 在发送请求之前做些什么
   * @param config 请求配置
   * @return config 请求配置
   */
  private runRequestInterceptors(config) {
    const funcs = [...GrpcCreator.interceptors.request.interceptorList.keys()]
    if (funcs.length === 0) return config
    let newConfig = null
    while(funcs.length > 0) {
      const currentFunc = funcs.shift()
      if (typeof currentFunc === 'function') {
        let currentConfig = currentFunc(config)
        // 中断拦截器及后续操作
        if (currentConfig?.error) {
          return currentConfig
        }
        newConfig = currentConfig || newConfig
      }
    }
    return newConfig || config
  }

  /**
   * 请求错误拦截器:对请求错误做些什么
   * @param err 请求错误信息
   * @return err 请求错误信息
   */
  private runRequestErrInterceptors(err) {
    let funcs = [...GrpcCreator.interceptors.request.interceptorList.values()]
    const errFuncs = funcs.map(item => {
      if(item[1] || typeof item[1] === 'function') return item[1]
    })
    if (errFuncs.length === 0) return err;
    let newErr = null
    while(errFuncs.length > 0) {
      const currentFunc = errFuncs.shift()
      if (typeof currentFunc === 'function') {
        let currentErr = currentFunc(err)
        if (currentErr?.error) {
          return currentErr?.data || {}
        }
        newErr = currentErr || newErr
      }
    }
    return newErr || err
    // errFuncs.forEach((item) => item(err))
  }


  /**
   * 响应拦截器: 对响应数据做点什么
   * @param response 响应数据
   * @return response 响应数据
   */
  private runResponseInterceptors(response) {
    const funcs = [...GrpcCreator.interceptors.response.interceptorList.keys()]
    if (funcs.length === 0) return response
    let newResponse = null;
    while(funcs.length > 0) {
      const currentFunc = funcs.shift()
      if (typeof currentFunc === 'function') {
        let currentRes = currentFunc(response)
        if (currentRes?.error) {
          return currentRes
        }
        newResponse = currentRes || newResponse
      }
    }
    return newResponse || response
  }

  /**
   * 响应错误拦截器: 超出 2xx 范围的状态码都会触发该函数
   * @param data 响应数据
   * @return data 响应数据
   */
  private runResponseErrInterceptors(data) {
    let funcs = [...GrpcCreator.interceptors.response.interceptorList.values()]
    const errFuncs = funcs.map(item => {
      if(item[1] || typeof item[1] === 'function') return item[1]
    })
    if (errFuncs.length === 0) return data;
    let newData = null
    while(errFuncs.length > 0) {
      const currentFunc = errFuncs.shift()
      if (typeof currentFunc === 'function') {
        let currentData = currentFunc(data)
        if (currentData?.error) {
          return currentData?.data || {}
        }
        newData = currentData || newData
      }
    }
    return newData || data
  }

  /**
   * 发送请求
   * @param requestClass 请求类
   * @param method 请求方法
   * @param metadata {Metadata} 请求头
   * @param timeout {number} 超过时间
   * @return {Promise} 响应请求的promise
   */
  sendRequest(requestClass, method, data, metadata = {}, timeout?: number) {
    // 加载默认metadata
    if (GrpcCreator.defaults.metadata) metadata = {...GrpcCreator.defaults.metadata, ...metadata}

    // 加载默认超时时间
    if (!timeout && GrpcCreator.defaults.timeout) timeout = GrpcCreator.defaults.timeout

    // 执行请求拦截器函数
    const config = this.runRequestInterceptors({ method, data, metadata, timeout })

    // 中断请求拦截器后续的操作
    if(config.error) return Promise.reject(config.data)

    metadata = config.metadata
    data = config.data
    method = config.method
    timeout = config.timeout

    if (typeof this.client[method] !== 'function') {
      return Promise.reject(`没有${method}这个方法`)
    }

    // 处理metadata
    const newMetadata: Metadata = resolveMetadata(metadata)

    // 设置请求体数据
    const request = new requestClass()
    Object.entries(data).forEach(([key, val]) => {
      if (typeof key === 'string') {
        const setFunc = request[`set${key.charAt(0).toUpperCase()}${key.slice(1)}`]
        if (typeof setFunc === 'function') {
          setFunc.call(request, val)
        }
      }
    })

    // this 指向
    let that = this
    // 请求示例
    let clientInstance = null
    let grpcPromise = null
    // 发送请求
    grpcPromise = new Promise<ResponseDataType>((resolve, reject) => {
      clientInstance = that.client[method](request, newMetadata, (err, res) => {
        if (err) {
          const newErr = that.runRequestErrInterceptors(err)
          reject(newErr)
        } else {
          // 包装响应数据
          const resObject = res.toObject()
          const responseData: ResponseDataType = {
            data: resObject,
            status: resObject.code ?? 200,
            statusText: resObject.message ?? 'OK',
            request: request,
            config
          }

          // 响应错误拦截器(code >= 300)
          if (responseData?.status >= 300) {
            return reject(that.runResponseErrInterceptors(responseData))
          }

          // 响应数据拦截器
          const newRes = that.runResponseInterceptors(responseData)
          if (newRes.error) {
            // 中断响应拦截器后续的操作
            reject(newRes.data)
          } else {
            resolve(newRes)
          }
        }
      })
    })
    // 取消请求cancal的方法挂载到promise上
    grpcPromise.cancel = () => {
      clientInstance.cancel.bind(clientInstance)()
      return `${method}请求取消`
    }

    // 请求超时的毫秒数
    if (timeout) {
      setTimeout(() => {
        return grpcPromise.cancel()
      }, timeout)
    }
    return grpcPromise


    // const response = this.client[method](request, metadata).then(res => {
    //   // 包装响应数据
    //   const resObject = res.toObject()
    //   const responseData: ResponseDataType = {
    //     data: resObject,
    //     status: resObject.code ?? 200,
    //     statusText: resObject.message ?? 'OK',
    //     metadata: metadata || null,
    //     request: request,
    //   }

    //   // 响应错误拦截器(code >= 300)
    //   if (responseData?.status >= 300) {
    //     return Promise.reject(this.runResponseErrInterceptors(responseData))
    //   }

    //   // 响应数据拦截器
    //   const newRes = this.runResponseInterceptors(responseData)
    //   return newRes
    // }).catch(err => {
    //   // 请求错误拦截器
    //   this.runRequestErrInterceptors(err)
    //   return Promise.reject(err)
    // })
    // return response
  }
}

// 初始化拦截器
GrpcCreator.interceptors = {
  request: new Interceptors(),
  response: new Interceptors()
}
// 初始化默认配置
GrpcCreator.defaults = {}
